上QQ阅读APP看书,第一时间看更新
第2章 自己动手写全文检索
很多软件系统都需要对应的数据结构。信息检索中最常用的数据结构是倒排索引。全文索引如图2-1所示。
图2-1 以词为基础的全文索引
倒排索引就是一个词到文档列表的映射。用HashMap实现的一个简单的倒排索引代码如下。
public class InvertedIndex { Map<String, List<Tuple>> index = new HashMap<String, List<Tuple>>(); //词和这个词在文档中出现的位置信息 // 索引文档 public void indexDoc(String docName, ArrayList<String> words) { int pos = 0; for (String word : words) { pos++; // 位置 List<Tuple> idx = index.get(word); if (idx == null) { idx = new LinkedList<Tuple>(); index.put(word, idx); } idx.add(new Tuple(docName, pos)); System.out.println("indexed " + docName + " : " + pos + " words"); } } // 搜索 public void search(List<String> words) { for (String word : words) { Set<String> answer = new HashSet<String>(); List<Tuple> idx = index.get(word); if (idx ! = null) { for (Tuple t : idx) { //找到了一些文档 answer.add(t.docName); } } System.out.print(word); for (String f : answer) { System.out.print(" " + f); //输出文件名 } System.out.println(""); } } private class Tuple { //<文档名,位置>元组 private String docName; // 文档名 private int position; // 位置 public Tuple(String d, int position) { this.docName = d; this.position = position; } } }
如果用户的查询中包含多个词,需要统计这些词在文档中出现的区间大小。区间越小的文档相关度越高。
public class Span { public int start; // 开始位置 public int end; // 结束位置 public Span(int s, int e) { start = s; end = e; } public int length() { return end - start + 1; } public String toString() { return start + "-" + end; } }
建立索引往往很耗时,所以应把建立好的倒排索引保存到文件。查询之前先读入建立好的索引。
倒排索引由两个文件组成:一是文件存储倒排列表;另外一个是B树(Btree)存储词到倒排列表的映射。
索引的实现接口如下。
/** 一个索引应该实现的功能模板 */ public interface Index { /** 使用数据库统计类构建索引 */ public void build(DatabaseStatistics s); /** 从文件加载倒排索引 */ public void read(String filename) throws IOException; /** 把内存中建好的倒排索引存入文件 */ public void flush(String filename) throws IOException; }
可以把创建索引和读入索引的方法分到不同的类实现,分别为IndexWriter和IndexReader类。