全文搜索之lucence入门(二) 索引创建

之前我们讲了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 的类型来重新需要的方法就好了


猜你喜欢

转载自blog.csdn.net/u012477338/article/details/80350209