手把手教你简单实现倒排索引


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.StringTokenizer;



/**
 * 
 * 存储格式:[word:{document-field-position}]
 * @author Administrator
 *
 */
public class InvertedIndex {
	
	private Map<String,Integer> fieldsMap;
	private Map<String,List<String>> index;
	private static long indexNo;
	
	public void write(List<Field> fields){
		for(Field field : fields){
			String fieldKey = field.getKey();
			Integer fieldIndex = getFieldIndex(fieldKey);
			
			String fieldValue = field.getValue();
			String[] values = tokenize(fieldValue);
			if(null != values && values.length>0){
				for(int i=0;i<values.length;i++){
					String currentIndex = indexNo+"-"+fieldIndex+"-"+i;
					String key = values[i];
					List<String> indexes = null;
					if(null == index){
						index = new HashMap<String, List<String>>();
						indexes = new ArrayList<String>();
					}else{
						if(index.containsKey(key)){
							indexes = index.get(key);
						}else{
							indexes = new ArrayList<String>();
						}
					}
					indexes.add(currentIndex);
					index.put(key,indexes);
				}
			}
		}
	}
	
	public long getCurrentIndexNo(){
		if(indexNo<=0){
			indexNo = index.size();
		}
		return indexNo;
	}
	
	public String[] tokenize(String value){
		String[] values = null;
		StringTokenizer tokenizer = new StringTokenizer(value);
		values = new String[tokenizer.countTokens()];
		for(int i=0;i<values.length;i++){
			values[i] = tokenizer.nextToken();
		}
		return values;
	}
	
	public Integer getFieldIndex(String fieldKey){
		if(null == fieldsMap)
			fieldsMap = new HashMap<String,Integer>();
		
		Integer fieldIndex = fieldsMap.get(fieldKey);
		if(null==fieldIndex||fieldIndex==0){
			int maxIndex = fieldsMap.size();
			fieldIndex = maxIndex+1;
			fieldsMap.put(fieldKey, fieldIndex);
		}
		return fieldIndex;
	}
	
	public List<String> search(String field, String key){
		int fieldIndex = getFieldIndex(field);
		List<String> indexes = index.get(key);
		String reg = "[\\d]-"+fieldIndex+"-[\\d]";
		List<String> result = new ArrayList<String>();
		for(String i : indexes){
			if(i.matches(reg)){
				result.add(i);
			}
		}
		return result;
	}
	
	public void print(){
		for(Entry<String,List<String>> entry : index.entrySet()){
			List<String> value = entry.getValue();
			for(String v : value){
				System.out.println(entry.getKey()+":"+v);
			}
		}
	}
	
	public static void main(String[] args) {
		Field title = new Field("title","this is an invertedIndex example");
		Field content = new Field("content", "here is the example so we can know how it work");
		List<Field> fields = new ArrayList<Field>();
		fields.add(title);
		fields.add(content);
		
		InvertedIndex invertedIndex = new InvertedIndex();
		invertedIndex.write(fields);
		invertedIndex.print();
		System.out.println("========================================");
		List<String> result = invertedIndex.search("title", "is");
		for(String r : result){
			System.out.println("search result:"+r);
		}
	}
	
}
public class Field {
 
 public Field(String key,String value) {
 setKey(key);
 setValue(value);
 }
 
 private String key;
 private String value;
 
 public String getKey() {
 return key;
 }
 public void setKey(String key) {
 this.key = key;
 }
 public String getValue() {
 return value;
 }
 public void setValue(String value) {
 this.value = value;
 }
}

 
 
 
 
 
  
  
  
  

运行结果:

手把手教你简单实现倒排索引

倒排索引的存储格式为:关键词:[出现的文本记录号,出现的字段号,在文档中所处的位置]

第一个字段用于快速获取所有相关文本记录,第二个字段用于用户的一些搜索条件过滤(如:用户只想搜索标题)、后期权重设置、搜索结果打分排序,第三个字段可结合第二个字段进行打分排序,还可用于截取前后文摘要。事实上还有其他字段,不限于以上所述功能,例子只用于帮助理解倒排索引。

其中index里关键词的排序可以进一步按照字母顺序排,可以以:

red

~is

~flag

man

~chester

这样的格式去存储,其中~为其前面一个不含~的词,即二、三、五行内容为redis,redflag,manchester,这样的存储可进一步减少所需空间并且由于字母有顺序,可以将搜索的关键词字符串进一步打散,更加快速的定位其在索引中的位置。

同理其后的字段也可以进行一些类似的优化。

Field还能增加一些辅助字段,例如此字段需不需要存储(例中原文本都没有存储),需不需要分词处理(如中文的话,会进行一些分词的处理,可用IK,paoding等分词工具)。

你可能感兴趣的:(手把手教你简单实现倒排索引)