`
suichangkele
  • 浏览: 199781 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

solr对docValue的使用

阅读更多

前面写了十来篇博客都是介绍lucene的docValue的,有五中docValue,但是在solr里面却只有一个配置,即在schema.xml中的<field>中添加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<IndexableField> createFields(SchemaField sf, Object value, float boost) {
	if (sf.hasDocValues()) { //在schema中含有docValue的情况
		List<IndexableField> 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<IndexableField> createFields(SchemaField field, Object value, float boost) {
	if (field.hasDocValues()) {//如果含有docValue
		List<IndexableField> 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<IndexableField> 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.<IndexableField> emptyList() : Collections.singletonList(f);
}

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

 

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

分享到:
评论

相关推荐

    solr在java中使用总结

    &lt;env-entry-value&gt;C:/software/solr-4.9.0/example&lt;/env-entry-value&gt; &lt;env-entry-type&gt;java.lang.String ``` - 其中`solr/home`的值应替换为你实际的Solr解压路径。 5. **拷贝必要的JAR包:** - 将solr-...

    solr配置和solrJ的使用

    - **操作详情**: 打开解压后的`solr`目录下的`web.xml`文件,设置`env-entry-value`属性为`SolrHome`目录路径,如`D:\WORK\SolrHome\solr`。 **6. 修改索引目录配置** - **步骤说明**: 在Solr配置文件中指定索引...

    solr1.4教程

    1.2.5 缓存:Solr内置了多种缓存机制,如查询结果缓存、文档ID到DocValue的缓存等,显著提高了搜索性能。 1.2.6 复制:Solr支持主从复制,确保数据的安全性和高可用性,可以轻松地扩展集群规模。 1.2.7 管理接口:...

    最新Solr环境搭建与IKAnalyzer3分词整合.doc

    &lt;Environment name="solr/home" type="java.lang.String" value="/usr/local/apache-tomcat-6.0.33/solr" override="true" /&gt; ``` 其中,docBase 为 Solr 的上传目录。 五、IKAnalyzer 安装 IKAnalyzer 的下载...

    solr自定义评分组件demo.zip

    通常,我们需要继承Solr的`FunctionQuery`类或`ValueSource`类,并覆盖其中的方法,以实现自己的评分算法。例如,可以基于文档的某些字段值、时间戳或者其他业务指标来计算评分。此外,还需要提供对应的解析器...

    详解spring中使用solr的代码实现

    如果你不打算使用Spring Data Solr提供的模板方式,而是直接使用Solr的原生API,可以自定义`SolrCloudServerFactoryBean`类,实现`FactoryBean&lt;SolrServer&gt;`和`InitializingBean`接口,以便在Spring容器初始化时进行...

    solrNet使用

    本教程将详细讲解SolrNet的使用,涵盖如何进行文档的新增、删除、更新以及连接Solr服务器的基本操作。 一、SolrNet介绍 SolrNet是基于HTTP协议的Solr客户端,支持多种.NET框架,如.NET Framework和.NET Core。它...

    java操作solr实现查询功能的实例

    在使用 Solr 时,需要正确地设置 Solr_URL,以便正确地连接到 Solr 服务器。如果 URL 后边不加表,将报错。 本实例展示了如何使用 Java 操作 Solr 实现查询功能,包括封装的查询方法和主函数。这种方法可以便于使用...

    UpdateSolrField.rar

    - 对于多值字段,可以多次调用`addField`或使用`addFields`方法传递一个字段名和值的列表,如:`doc.addField("multiValuedField", value1, value2, ...)` 5. **发送更新请求**: - 创建`UpdateRequest`对象,...

    Lucene笔记

    单独使用Lucene无法构建出完整的互联网搜索引擎,因为它缺乏对网络上的海量数据进行爬取、存储及管理的能力。这通常需要额外的技术来实现,比如网络爬虫(网络蜘蛛),它能够在互联网上自动抓取网页内容并将其存储到...

Global site tag (gtag.js) - Google Analytics