solr对docValue的使用

阅读更多

前面写了十来篇博客都是介绍lucene的docValue的,有五中docValue,但是在solr里面却只有一个配置,即在schema.xml中的中添加docValues=true,那么solr在建立索引的时候到底是使用了什么类型的docValue呢?看下源码就知道了。

solr在添加document的时候,是使用了DirectUpdateHandler2这个类的

public int addDoc(AddUpdateCommand cmd)

 方法,从这个类中一步一步的debug,终于知道了solr是如何使用docValue的。solr在添加document的时候,要将客户端传递过来的SolrInputDocument转变为lucene的document,将solr的field变为lucene的field,对docValue的使用就在这里,上面的addDoc方法的参数cmd中,就有获得lucene的document的方法,AddUpdateCommand.getLuceneDocument()

public Document getLuceneDocument() {
	return DocumentBuilder.toDocument(getSolrInputDocument(), req.getSchema());
}

上面的方法的名字很形象的说明了是要获得lucene的document,在toDocument方法里面就有根据solr的chemafield 转变为lucene的field的方法,如下:

// 
private static void addField(Document doc, SchemaField field, Object val, float boost) {
	if (val instanceof IndexableField) {
		// set boost to the calculated compound boost
		((Field) val).setBoost(boost);
		doc.add((Field) val);
		return;
	}
	for (IndexableField f : field.getType().createFields(field, val, boost)) {
		if (f != null)
			doc.add((Field) f); // null fields are not added
	}
}lei

 上面中最关键的就是field.getType.createFields方法,即根据SchemaField的FieldType获得lucene的field,所以只需要看一下几个常用的FieldType就可以了,在工作中最常用的是三个,一个是数字类型的,一个是字符串(String)类型的,一个是Text类型的(也是字符串换,不过Text类型的是分词的)。

 

1、数字类型的: 

        对于int,float,long,double,还是tint,tfloat,tlong,tdouble,他们的父类都是TrieField,完整名称是:org.apache.solr.schema.TrieField,看看他的createFields(SchemaField, Object, float)方法:

public List createFields(SchemaField sf, Object value, float boost) {
	if (sf.hasDocValues()) { //在schema中含有docValue的情况
		List fields = new ArrayList<>();
		final IndexableField field = createField(sf, value, boost);//这个是根据配置产生一个intField或者longFiled,这个是不带有docValue的。具体看下一个方法
		fields.add(field);
		if (sf.multiValued()) {//如果这个值是multiValued的
			BytesRef bytes = new BytesRef();
			readableToIndexed(value.toString(), bytes);
			fields.add(new SortedSetDocValuesField(sf.getName(), bytes));//再创建一个SortedSetDocValue的field。
		} else {//如果是单值域的
			final long bits;
			if (field.numericValue() instanceof Integer || field.numericValue() instanceof Long) {//
				bits = field.numericValue().longValue();
			} else if (field.numericValue() instanceof Float) {
				bits = Float.floatToIntBits(field.numericValue().floatValue());
			} else {
				assert field.numericValue() instanceof Double;
				bits = Double.doubleToLongBits(field.numericValue().doubleValue());
			}
			fields.add(new NumericDocValuesField(sf.getName(), bits));//创建一个简单的NumericDocValue
		}
		return fields;
	} else {//没有docValue的情况
		return Collections.singletonList(createField(sf, value, boost));
	}
}

 在上面可以看到,如果这个域是docValue的(即在schemField中写了docValues=true),则会额外的创建一个docValue的域,如果是多值域的,则会创建一个SortedSetDocValued类型的,最后返回的list里面含有两个field;如果是单值域,则会创建一个NumericDocValue类型的。

下面是调用的产生一个不带有docValue的field的方法,

public IndexableField createField(SchemaField field, Object value, float boost) {
	boolean indexed = field.indexed();
	boolean stored = field.stored();
	boolean docValues = field.hasDocValues();
	if (!indexed && !stored && !docValues) {//如果不建立索引,或者不存储,或者没有dcoValues则不会创建任何东西,换句话说,如果一个域要想有意义,则至少简历索引,或者至少保存或者简历docValue。
		if (log.isTraceEnabled()) log.trace("Ignoring unindexed/unstored field: " + field);
		return null;
	}
	FieldType ft = new FieldType();//创建fieldType,可以发现,在数字类型的fieldType中,仅仅是保存了五个属性,存储、分词、建索引、忽略标准因子、倒排表的格式,不过是没有词向量的
	ft.setStored(stored);
	ft.setTokenized(true);
	ft.setIndexed(indexed);
	ft.setOmitNorms(field.omitNorms());
	ft.setIndexOptions(getIndexOptions(field, value.toString()));
	switch (type) {
	case INTEGER:
		ft.setNumericType(NumericType.INT);
		break;
	case FLOAT:
		ft.setNumericType(NumericType.FLOAT);
		break;
	case LONG:
		ft.setNumericType(NumericType.LONG);
		break;
	case DOUBLE:
		ft.setNumericType(NumericType.DOUBLE);
		break;
	case DATE:
		ft.setNumericType(NumericType.LONG);
		break;
	default:
		throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
	}
	ft.setNumericPrecisionStep(precisionStep);
	final org.apache.lucene.document.Field f;
	switch (type) {
	case INTEGER:
		int i = (value instanceof Number) ? ((Number) value).intValue() : Integer.parseInt(value.toString());
		f = new org.apache.lucene.document.IntField(field.getName(), i, ft);//创建具体的域,将ft作为一个参数传入。
		break;
	case FLOAT:
		float fl = (value instanceof Number) ? ((Number) value).floatValue() : Float.parseFloat(value.toString());
		f = new org.apache.lucene.document.FloatField(field.getName(), fl, ft);
		break;
	case LONG:
		long l = (value instanceof Number) ? ((Number) value).longValue() : Long.parseLong(value.toString());
		f = new org.apache.lucene.document.LongField(field.getName(), l, ft);
		break;
	case DOUBLE:
		double d = (value instanceof Number) ? ((Number) value).doubleValue()
				: Double.parseDouble(value.toString());
		f = new org.apache.lucene.document.DoubleField(field.getName(), d, ft);
		break;
	case DATE:
		Date date = (value instanceof Date) ? ((Date) value) : dateField.parseMath(null, value.toString());
		f = new org.apache.lucene.document.LongField(field.getName(), date.getTime(), ft);
		break;
	default:
		throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
	}
	f.setBoost(boost);
	return f;
}

总结一下数字类型的schemaField,如果没有dcoValues,则仅仅会根据类型创建一个普通类型的lucene的field, 如果有docValue的话,则会创建两个field,一个是普通的field,第二个是含有docValue的,这两个field含有相同的名字和内容,在建立索引的时候会同时使用两个域。如果是多值域的,则会创建一个SortedSetDocValue,如果是单值域的,则会创建一个NumericDocValue的。在创建一个普通的不带有docValue的field的时候,仅仅会保存五个属性,不过没有词向量。

 

2、String类型的

       String类型的是不分词的,他也是有docValue的,对应的fieldType是:org.apache.solr.schema.StrField。看看他的createFields方法吧

public List createFields(SchemaField field, Object value, float boost) {
	if (field.hasDocValues()) {//如果含有docValue
		List fields = new ArrayList<>();
		fields.add(createField(field, value, boost));//普通的value
		final BytesRef bytes = new BytesRef(value.toString());
		if (field.multiValued()) {//如果是多值域的,则会创建一个SortedSetDocValue
			fields.add(new SortedSetDocValuesField(field.getName(), bytes));
		} else {
			fields.add(new SortedDocValuesField(field.getName(), bytes));//如果是单值域的,则会创建一个SortedDocValue。
		}
		return fields;
	} else {
		return Collections.singletonList(createField(field, value, boost));
	}
} 

另外,String类型的createField方法没有覆写FieldType的自己的createField方法,如下:

public IndexableField createField(SchemaField field, Object value, float boost) {
	if (!field.indexed() && !field.stored()) {
		if (log.isTraceEnabled())
			log.trace("Ignoring unindexed/unstored field: " + field);
		return null;
	}
	String val;
	try {
		val = toInternal(value.toString());
	} catch (RuntimeException e) {
		throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,"Error while creating field '" + field + "' from value '" + value + "'", e);
	}
	if (val == null)
		return null;

	org.apache.lucene.document.FieldType newType = new org.apache.lucene.document.FieldType();
	newType.setIndexed(field.indexed());//是否建立索引
	newType.setTokenized(field.isTokenized());//是否分词
	newType.setStored(field.stored());//是否存储
	newType.setOmitNorms(field.omitNorms());//是否忽略标准因子
	newType.setIndexOptions(getIndexOptions(field, val));//倒排表的格式
	newType.setStoreTermVectors(field.storeTermVector());//是否存储词向量
	newType.setStoreTermVectorOffsets(field.storeTermOffsets());//是否存储词向量的偏移量
	newType.setStoreTermVectorPositions(field.storeTermPositions());//是否存储词向量的位置增量。可以发现字符串类型的是可以记录词向量的。

	return createField(field.getName(), val, newType, boost);//这个很简单,忽略
}

总结一下字符串类型的,如果有docValue的话,也是有两个域,一个是普通的没有docValue的域,一个是含有docValue的域,含有docValue的域按照是否是多值域,分为sortedSetDocValue和SortedDocValue。另外和数字类型的field不同的是,字符串类型的是可以有词向量的。

 

3、Text类型的

这个类是可以分词的域,根据常识的话,他不会使用docValue,因为一个域既然分词的话,做facet、sort是没有意义的,不过还是看下代码吧。

在TextField的类中没有覆盖FieldType的createFields方法,所以我们看下FieldType的createFields方法:

public List createFields(SchemaField field, Object value, float boost) {
	IndexableField f = createField(field, value, boost);
	if (field.hasDocValues() && f.fieldType().docValueType() == null) {
		throw new UnsupportedOperationException("This field type does not support doc values: " + this);
	}
	return f == null ? Collections. emptyList() : Collections.singletonList(f);
}

 他也会调用createField(注意没有s)方法,但是在TextField中也没有覆写createField方法, 从上面的String类型的createField方法来看,如果不覆盖FieldType中的createField方法,是不会创建一个含有docValue的field的。这和常识也是匹配的。

 

如此简单,看完了solr在建立索引的时候是如何使用docValue的。

你可能感兴趣的:(solr,docValue,docVlaues)