您的位置:首页 > 其它

lucene5.1 fst源码分析(fst接口方法写入和读取测试)

2017-04-22 15:41 447 查看
1 fst基本概念
1-1 节点node和弧arc

2 lucene中的fst测试代码

3 源码分析
3-1 util的get方法分析

3-2 FST的findTargetArc方法
3-2-1 类成员变量介绍

3-2-2方法原型

3-2-3源码分析

3-3 readFirstRealTargetArc方法

3-3 readNextRealArc方法

.1 fst基本概念

有限状态机

.1-1 节点(node)和弧(arc)

node包含了进入该node的0个或者多个arc,也包含了从该node走出的0个或者多个arc

.2 lucene中的fst测试代码

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.core.WhitespaceTokenizer;
import org.apache.lucene.analysis.synonym.SynonymFilterFactory;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.util.FilesystemResourceLoader;
import org.apache.lucene.store.DataInput;
import org.apache.lucene.store.InputStreamDataInput;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.CharsRef;
import org.apache.lucene.util.IntsRef;
import org.apache.lucene.util.IntsRefBuilder;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.util.Version;
import org.apache.lucene.util.fst.Builder;
import org.apache.lucene.util.fst.ByteSequenceOutputs;
import org.apache.lucene.util.fst.FST;
import org.apache.lucene.util.fst.FST.INPUT_TYPE;
import org.apache.lucene.util.fst.PositiveIntOutputs;
import org.apache.lucene.util.fst.Util;

public class FSTTest2 {

public static void main(String[] args) throws IOException {

String inputValues[] = {"mop", "moth", "pop","star","stop","top"};//输入
long outputValues[] = {5, 7, 12, 14, 18,20};//输出。在lucene中即为term后缀在tim文件中的偏移量

//      String inputValues[] = { "xstop", "xstopxx", "yxxxstop" };
//      long outputValues[] = { 12, 10, 6};

PositiveIntOutputs outputs = PositiveIntOutputs.getSingleton();
Builder<Long> builder = new Builder<Long>(INPUT_TYPE.BYTE1, outputs);
BytesRef scratchBytes = new BytesRef();
IntsRefBuilder ifb = new IntsRefBuilder();
IntsRefBuilder scratchInts = new IntsRefBuilder();
BytesRefBuilder bfb = new BytesRefBuilder();

//插入输入和输出到fst中
for (int i = 0; i < inputValues.length; i++) {

CharSequence cs = inputValues[i];
NumericUtils.intToPrefixCodedBytes(inputValues[i].length(), 0, bfb);
builder.add(Util.toUTF32(cs, ifb), outputValues[i]);
}
//构建字节流索引
FST<Long> fst = builder.finish();
Long value = Util.get(fst, new BytesRef("stop"));//根据输入得到输出
System.out.println(value);
}
}


.3 源码分析

.3-1 util的get方法分析

public static<T> T get(FST<T> fst, BytesRef input) throws IOException {
assert fst.inputType == FST.INPUT_TYPE.BYTE1;

final BytesReader fstReader = fst.getBytesReader();

// TODO: would be nice not to alloc this on every lookup
final FST.Arc<T> arc = fst.getFirstArc(new FST.Arc<T>());

// Accumulate output as we go
T output = fst.outputs.getNoOutput();//初始化输出
for(int i=0;i<input.length;i++) {
if (fst.findTargetArc(input.bytes[i+input.offset] & 0xFF, arc, arc, fstReader) == null) {//寻找一个字符,即寻找一个arc
return null;
}
output = fst.outputs.add(output, arc.output);//累加每个弧上的输出
}

if (arc.isFinal()) {
return fst.outputs.add(output, arc.nextFinalOutput);
} else {
return null;
}
}


.3-2 FST的findTargetArc方法

.3-2-1 类成员变量介绍



.3-2-2方法原型:

private Arc findTargetArc(int labelToMatch, Arc follow, Arc arc, BytesReader in, boolean useRootArcCache)

其中的labelToMatch是要查找的byte,一般是一个字符,follow是前一个弧,在执行中只提供自己的target,即follow的tonode的起始地址,目标arc肯定挂在这个tonode下。arc是要查找的弧,在传入时,和follow相同,在后续的执行中该arc的内部变量会被不断替换,最后返回的也是这个arc。

.3-2-3源码分析



in.setPosition(getNodeAddress(follow.target));

arc.node = follow.target;//follow的tonode就是目标弧的fromnode

// System.out.println("fta label=" + (char) labelToMatch);

// Linear scan
readFirstRealTargetArc(follow.target, arc, in);//例如找到上图中最左边那个假节点的第一个真的目标弧,例如本次会找到m

while(true) {
//System.out.println("  non-bs cycle");
// TODO: we should fix this code to not have to create
// object for the output of every arc we scan... only
// for the matching arc, if found
if (arc.label == labelToMatch) {
//System.out.println("    found!");
return arc;//匹配上就返回这个arc
} else if (arc.label > labelToMatch) {
return null;
} else if (arc.isLast()) {
return null;
} else {
readNextRealArc(arc, in);//没有匹配上label,就继续找
}
}


.3-3 readFirstRealTargetArc方法

public Arc<T> readFirstRealTargetArc(long node, Arc<T> arc, final BytesReader in) throws IOException {
//这里的node,是上一次匹配上的那个弧的tonode,要找下一个匹配弧,就要在这个tonode的弧里找,所以把这个地址所以本次查找的起始地址
final long address = getNodeAddress(node);
in.setPosition(address);
//System.out.println("  readFirstRealTargtArc address="
//+ address);
//System.out.println("   flags=" + arc.flags);
arc.node = node;//这个node是上一个弧的targetnode,也是本次弧的fromnode(1)

if (in.readByte() == ARCS_AS_FIXED_ARRAY) {
//System.out.println("  fixedArray");
// this is first arc in a fixed-array
arc.numArcs = in.readVInt();
if (packed || version >= VERSION_VINT_TARGET) {
arc.bytesPerArc = in.readVInt();
} else {
arc.bytesPerArc = in.readInt();
}
arc.arcIdx = -1;
arc.nextArc = arc.posArcsStart = in.getPosition();
//System.out.println("  bytesPer=" + arc.bytesPerArc + " numArcs=" + arc.numArcs + " arcsStart=" + pos);
} else {
//arc.flags = b;
arc.nextArc = address;//一个弧的nextArc,是这个弧的fromnode的下一个弧。但是这个弧在构造阶段,nextArc就是他自己
arc.bytesPerArc = 0;
}

return readNextRealArc(arc, in);//该行以上代码初始化了一个arc的fromnode((1)处),也初始化了一个arc的nextArc位置,下来就是要给arc的其他变量赋值,就在这个方法中


.3-3 readNextRealArc方法

// this is a continuing arc in a fixed array
if (arc.bytesPerArc != 0) {
// arcs are at fixed entries
arc.arcIdx++;
assert arc.arcIdx < arc.numArcs;
in.setPosition(arc.posArcsStart);
in.skipBytes(arc.arcIdx*arc.bytesPerArc);
} else {
// arcs are packed //packed的arc表示什么意思???
in.setPosition(arc.nextArc);//在初始阶段,一个arc的nextArc表示这个arc的fromnode的第一个弧的位置,所以可以作为这个arc的起始位置(这个arc的fromnode的真正的nextArc在后边会设置)
}
arc.flags = in.readByte();//读取flags
arc.label = readLabel(in);//读取label

//判断是否有输出
if (arc.flag(BIT_ARC_HAS_OUTPUT)) {
arc.output = outputs.read(in);
} else {
arc.output = outputs.getNoOutput();
}
//判断是否有final_output
if (arc.flag(BIT_ARC_HAS_FINAL_OUTPUT)) {
arc.nextFinalOutput = outputs.readFinalOutput(in);
} else {
arc.nextFinalOutput = outputs.getNoOutput();
}
//是否是stopnode
if (arc.flag(BIT_STOP_NODE)) {
if (arc.flag(BIT_FINAL_ARC)) {
arc.target = FINAL_END_NODE;
} else {
arc.target = NON_FINAL_END_NODE;
}
arc.nextArc = in.getPosition();
} else if (arc.flag(BIT_TARGET_NEXT)) {//这个弧是否有target_next优化(target_next优化见下边解释)
arc.nextArc = in.getPosition();
// TODO: would be nice to make this lazy -- maybe
// caller doesn't need the target and is scanning arcs...
//如果有target_next优化,就需要调用者开始扫描该节点后续的所有弧,所有的弧都跳过后,就是当前弧的tonode起始地址
if (nodeAddress == null) {
if (!arc.flag(BIT_LAST_ARC)) {
if (arc.bytesPerArc == 0) {
// must scan
seekToNextNode(in);
} else {
in.setPosition(arc.posArcsStart);
in.skipBytes(arc.bytesPerArc * arc.numArcs);
}
}
//跳过了当前节点的所有弧后,就是当前节点的targe节点了,从这个target节点开始,就可以读当前弧的下一个弧了(不是next,是tonode的弧)
arc.target = in.getPosition();
} else {
arc.target = arc.node - 1;
assert arc.target > 0;
}
} else {
if (packed) {
final long pos = in.getPosition();
final long code = in.readVLong();
if (arc.flag(BIT_TARGET_DELTA)) {
// Address is delta-coded from current address:
arc.target = pos + code;
//System.out.println("    delta pos=" + pos + " delta=" + code + " target=" + arc.target);
} else if (code < nodeRefToAddress.size()) {
// Deref
arc.target = nodeRefToAddress.get((int) code);
//System.out.println("    deref code=" + code + " target=" + arc.target);
} else {
// Absolute
arc.target = code;
//System.out.println("    abs code=" + code);
}
} else {
arc.target = readUnpackedNodeTarget(in);//如果没有target_next优化,需要再读一个字节,就可以得到这个弧的tonode的起始地址了,这是一个seek过程
}
arc.nextArc = in.getPosition();
}
return arc;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  lucene
相关文章推荐