之前我们讲了lucence的一些基本概念,这次讲下索引的创建步骤。
//创建分词器
Analyzer analyzer = new SmartChineseAnalyzer();
//Analyzer ik = new IKAnalyzer(); ik分词器
//配置索引创建器
IndexWriterConfig config = new IndexWriterConfig(analyzer);
//获取索引的存储位置
Directory directory = FSDirectory.open((new File("d:/test/index1").toPath()));
//索引写入对象
IndexWriter writer = new IndexWriter(directory, config);
//创建字段类型
FieldType nameType = new FieldType();
//设置反向索引存储信息
nameType.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
//内容是否存储
nameType.setStored(true);
//是否分词
nameType.setTokenized(true);
//是否标准化
nameType.setOmitNorms(true);
//是否建立正向索引
nameType.setDocValuesType(DocValuesType.SORTED);
//是否存储词项向量信息
nameType.setStoreTermVectors(true);
//是否存储词项偏移量
nameType.setStoreTermVectorOffsets(true);
//是否存储词项位置
nameType.setStoreTermVectorPositions(true);
//是否存储词项附加信息
nameType.setStoreTermVectorPayloads(true);
//索引字段类型对象,不可以修改
nameType.freeze();
//创建字段
Field nameField = new Field("name2", "张三的英文名字是zhang San Leo", nameType){
@Override
public BytesRef binaryValue() {
return new BytesRef((String)this.fieldsData);
}
};
//创建文档
Document doc = new Document();
//将字段加入到文档中
doc.add(nameField);
//将文档加入到索引
writer.addDocument(doc);
//刷新
writer.flush();
writer.commit();
writer.close();
directory.close();
这是一个简单的索引创建步骤。在lucence中我们一个文档就是一条记录,一个文档里面会有多个字段。这就相当于我们数据库里面的一个表和表的多个字段。
索引存储lucence中有2中方式,一种是直接储存在文件中,像上面我们用的就是文件存储,还有一种就是RAM内存存储RAMDirectory。
这里在创建字段的时候我们覆盖了Field的binaryValue()方法,这里跟lucence的正向索引创建有关。为什么覆盖的这个方法不是其他的,这里我们跟踪源代码看下。
private void indexDocValue(PerField fp, DocValuesType dvType, IndexableField field) throws IOException {
if (fp.fieldInfo.getDocValuesType() == DocValuesType.NONE) {
// This is the first time we are seeing this field indexed with doc values, so we
// now record the DV type so that any future attempt to (illegally) change
// the DV type of this field, will throw an IllegalArgExc:
fieldInfos.globalFieldNumbers.setDocValuesType(fp.fieldInfo.number, fp.fieldInfo.name, dvType);
}
fp.fieldInfo.setDocValuesType(dvType);
int docID = docState.docID;
switch(dvType) {
case NUMERIC:
if (fp.docValuesWriter == null) {
fp.docValuesWriter = new NumericDocValuesWriter(fp.fieldInfo, bytesUsed);
}
((NumericDocValuesWriter) fp.docValuesWriter).addValue(docID, field.numericValue().longValue());
break;
case BINARY:
if (fp.docValuesWriter == null) {
fp.docValuesWriter = new BinaryDocValuesWriter(fp.fieldInfo, bytesUsed);
}
((BinaryDocValuesWriter) fp.docValuesWriter).addValue(docID, field.binaryValue());
break;
case SORTED:
if (fp.docValuesWriter == null) {
fp.docValuesWriter = new SortedDocValuesWriter(fp.fieldInfo, bytesUsed);
}
((SortedDocValuesWriter) fp.docValuesWriter).addValue(docID, field.binaryValue());
break;
case SORTED_NUMERIC:
if (fp.docValuesWriter == null) {
fp.docValuesWriter = new SortedNumericDocValuesWriter(fp.fieldInfo, bytesUsed);
}
((SortedNumericDocValuesWriter) fp.docValuesWriter).addValue(docID, field.numericValue().longValue());
break;
case SORTED_SET:
if (fp.docValuesWriter == null) {
fp.docValuesWriter = new SortedSetDocValuesWriter(fp.fieldInfo, bytesUsed);
}
((SortedSetDocValuesWriter) fp.docValuesWriter).addValue(docID, field.binaryValue());
break;
default:
throw new AssertionError("unrecognized DocValues.Type: " + dvType);
}
}
上面是lucence建立正向索引的方法,我们可以发现不同的DocValuesType lucence在获取field的值的时候是调用不一样的方法,我们可以看到DocValuesType.SORTED 对应的binaryValue的方法。在Field中binaryValue方法对应fieldData的类型必须是BytesRef类型。但是我们创建Field用的是Field(String name, String value, IndexableFieldType type)方法,这样就导致fieldDate类型是String。所以我们需要重写binaryValue()。如下源码
@Override
public BytesRef binaryValue() {
if (fieldsData instanceof BytesRef) {
return (BytesRef) fieldsData;
} else {
return null;
}
}
public Field(String name, String value, IndexableFieldType type) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
if (value == null) {
throw new IllegalArgumentException("value must not be null");
}
if (!type.stored() && type.indexOptions() == IndexOptions.NONE) {
throw new IllegalArgumentException("it doesn't make sense to have a field that "
+ "is neither indexed nor stored");
}
this.type = type;
this.name = name;
this.fieldsData = value;
}
从上面的源码我们可以发现其实我们可以不用Field(String name, String value, IndexableFieldType type)方法,用Field(String name, BytesRef bytes, IndexableFieldType type)方法来创建Field,这样就不用重写binaryValue()了。确实可以这样,但是BytesRef 类型是不可以做分词的,所以如果我们要分词,我们还是需要用第一种方法。我们可以根据DocValuesType 的类型来重新需要的方法就好了