问题是这样的,同事在用solr对一个字段进行facet操作的时候,报出了一个错误
you are intending to use sorting,facet,group or other statistic feature, please set field:[ex_payModeDetailName] docValue property 'true'
错误提示也很明显,就是在做facet时要把这个字段的docValue打开,所以就照着做了
重新做了一遍全量,发现还是报同样的错误,感觉非常奇怪,按理说这个错误也算很常见了,也很具体,别的字段这个错误这种方式就能解决,怎么这个不行呢,难道跟这个自定义字段有关系?
查看了一下这个自定义字段,就是从一个json的字段中,解析出各个key,重新组装成动态字段。所以去看了一下这个自定义字段的代码,发现了问题所在,核心代码如下
for (String key : json.keySet()) { field = new SchemaField(propPrefix + key, sf.getType(), OMIT_NORMS | OMIT_TF_POSITIONS | STORED | INDEXED, ""); fieldValue = String.valueOf(json.get(key)); if ("null".equalsIgnoreCase(fieldValue) || (filterKeys != null && !this.filterKeys.contains(key))) { continue; } result.add(docVal); } result.add(this.createField(field, fieldValue, 1.0f)); }
即在字段定义的时候,就定义了字段的类型为OMIT_NORMS | OMIT_TF_POSITIONS | STORED | INDEXED,并没有docValue,所以在schema.xml中定义并不能真正意义上的打开dcoValue的开关,好吧,找到了问题所在进行修改,对需要打开的json的key的field如下操作
field = new SchemaField(propPrefix + key, sf.getType(), OMIT_NORMS | OMIT_TF_POSITIONS | STORED | INDEXED | DOC_VALUES, "");
ok,又做了一次全量发布,让同事进行测试一下,很可惜,反馈依旧是这个错误,难以理解。
最后无奈咨询了一下百岁,他看了一下指出了问题所在。
在上面的代码中,我们定义了一个打开docValue的Field,然后调用了基类也就是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.setTokenized(field.isTokenized()); newType.setStored(field.stored()); newType.setOmitNorms(field.omitNorms()); newType.setIndexOptions(field.indexed() ? getIndexOptions(field, val) : IndexOptions.NONE); newType.setStoreTermVectors(field.storeTermVector()); newType.setStoreTermVectorOffsets(field.storeTermOffsets()); newType.setStoreTermVectorPositions(field.storeTermPositions()); newType.setStoreTermVectorPayloads(field.storeTermPayloads()); return createField(field.getName(), val, newType, boost); }
方法中并没有对我们设定的docValue值进行什么操作,所以证明我们的这种方式并没有真正的打开docValue,那该如何正确的打开docValue呢?参考一下StrField中的方式。
@Override public ListcreateFields(SchemaField field, Object value, float boost) { IndexableField fval = createField(field, value, boost); if (field.hasDocValues()) { IndexableField docval; final BytesRef bytes = new BytesRef(value.toString()); if (field.multiValued()) { docval = new SortedSetDocValuesField(field.getName(), bytes); } else { docval = new SortedDocValuesField(field.getName(), bytes); } // Only create a list of we have 2 values... if (fval != null) { List fields = new ArrayList<>(2); fields.add(fval); fields.add(docval); return fields; } fval = docval; } return Collections.singletonList(fval); }
即要自定义一个类似SortedSetDocValuesField的field,参考这个思路,修改了代码
for (String key : json.keySet()) { field = new SchemaField(propPrefix + key, sf.getType(), OMIT_NORMS | OMIT_TF_POSITIONS | STORED | INDEXED | DOC_VALUES, ""); fieldValue = String.valueOf(json.get(key)); if ("null".equalsIgnoreCase(fieldValue) || (filterKeys != null && !this.filterKeys.contains(key))) { continue; } if(this.docValueKeys != null && this.docValueKeys.contains(key)){ BytesRef bytes = new BytesRef(fieldValue); IndexableField docVal = new SortedDocValuesField(propPrefix + key,bytes); result.add(docVal); } result.add(this.createField(field, fieldValue, 1.0f)); }
再进行一次全量写入,解决了问题。
PROBLEM SOLVED!