Field ts is missing or is not configured as a fast field.
主线位置:查询主线里收集器和聚合读快速字段的那一步,
segment_reader.fast_fields()按名字和类型找到一列。列存的目录怎么按「字段名 + 类型码」组织,见快速字段。
flowchart TB
call["fast_fields i64 ts"] --> fast{"字段是 FAST 吗"}
fast -->|否| e1["is not configured as fast field"]
fast -->|是| col{"有 i64 的列吗"}
col -->|有| ok["Column"]
col -->|没有| e2["is missing or ..."]
实验 crate:exp-tantivy/pita-lab,src/bin/p08_fast.rs。schema:ts 是 u64、FAST | STORED;n 是 u64、INDEXED | STORED(不是快速字段);opt 是 u64、FAST,但没有一篇文档给它值。写两篇,在第一个段上取列,first(0) 取第一篇的值。M2 Max、macOS 27、rustc 1.99.0,release。
现象
ff.u64("ts"): Ok(Some(1760659200))
ff.i64("ts"): Err(SchemaError("Field `ts` is missing or is not configured as a fast field."))
ff.date("ts"): Err(SchemaError("Field `ts` is missing or is not configured as a fast field."))
ff.u64("n"): Err(InvalidArgument("Field \"n\" is not configured as fast field"))
ff.u64("opt"): Ok(None)
ff.u64("tss"): Err(SchemaError("Field `tss` is missing or is not configured as a fast field."))
ff.column_opt::<u64>("opt"): Ok(true)
ff.u64_lenient("ts"): Ok(Some((Some(1760659200), U64)))
TopDocs order_by_fast_field::<i64>("ts"): Err(SchemaError("Field `ts` is of type U64, not of the type I64."))
TopDocs order_by_fast_field::<u64>("ts"): Ok([(Some(1760659300), DocAddress { segment_ord: 0, doc_id: 1 }), (Some(1760659200), DocAddress { segment_ord: 0, doc_id: 0 })])
ts是快速字段,用i64或date去取,报「不存在或不是快速字段」。时间戳常被定义成u64,读的时候顺手写成i64,就是这一行。- 这句报错和字段名拼错(
tss)一字不差,单看错误分不出是哪种。 - 真的不是快速字段(
n)时,报的是另一句,类型也不同:InvalidArgument。 opt是快速字段但没有任何值:照样拿得到一列,first(0)是None,不报错。- 同一个类型错误交给排序收集器,报错直接写出了两种类型。
原因
FastFieldReaders::u64、i64、date 这些方法都只是一行 self.column(field),类型由返回值决定。column 交给 column_opt:先按名字解析字段,再按「字段名 + 想要的列类型」在列存里找。u64 字段只写了一列 u64,找 i64 的列找不到,返回 None,column 把所有 None 一律翻译成同一句话:
let col_opt: Option<Column<T>> = self.column_opt(field)?;
col_opt.ok_or_else(|| {
crate::TantivyError::SchemaError(format!(
"Field `{field}` is missing or is not configured as a fast field."
))
})
「字段名不存在」也走到这里:resolve_field 在 schema 里找不到名字时返回 Ok(None)。只有「字段存在但没设 FAST」在 resolve_field 里被单独拦下,报 Field "n" is not configured as fast field。所以第一句报错至少包含两种情况:名字错、类型错。column_opt 不对照 schema 的类型,只问列存「这个名字下有没有这种类型的列」;列存里一个名字下可以有几列,u64_lenient_for_type_all 的注释就写着「In case of JSON, there may be two columns. One for term and one for numerical types.」
排序收集器多做了一步。SortByStaticFastValue::check_schema 在开始收集之前对照 schema:
let schema_type = field_entry.field_type().value_type();
if schema_type != T::to_type() {
return Err(crate::TantivyError::SchemaError(format!(
"Field `{}` is of type {schema_type:?}, not of the type {:?}.",
self.field,
T::to_type()
)));
}
修法
- 按 schema 里的类型取:
add_u64_field的字段用.u64(..),add_date_field用.date(..)。在order_by_fast_field::<T>里也一样。 - 只是想拿一个能比较大小的值、不在乎原类型时,可以用
u64_lenient:它把 i64、u64、f64、日期的列都按映射后的u64打开,同时告诉你实际类型,上面的Ok(Some((Some(1760659200), U64)))。它带着#[doc(hidden)],不算稳定接口。 - 字段可能在某些段里根本没有值时,别把
Ok(None)(拿到列、这篇没值)和Err(没有这一列)混为一谈,opt那一行就是前者。
怎么检查
报这句错时,先排除名字错:schema.get_field("ts") 对不存在的名字返回 FieldNotFound。再把每个字段的类型和 FAST 打出来:
for (_, entry) in schema.fields() {
println!("check {:<2} type={:?} fast={}", entry.name(), entry.field_type().value_type(), entry.is_fast());
}
check ts type=U64 fast=true
check n type=U64 fast=false
check opt type=U64 fast=true
fast=true 而类型和你调的方法不一致,就是类型错。把读快速字段的代码集中在一处,用 schema 的 value_type() 决定调哪个方法,比在各处写死 i64 更不容易出错。
小结
fast_fields().i64("ts")对一个u64快速字段报「is missing or is not configured as a fast field」:列按名字加类型查找,类型不对就当作没有。- 同一句报错也用于字段名不存在;字段存在但没设
FAST是另一句InvalidArgument;order_by_fast_field的报错会直接写出两种类型。 - 按 schema 的类型取列,或者用
u64_lenient;排查时先打value_type()和is_fast()。