吐槽一下Lucene的Field接口变化(更新再吐槽)


Lucene是老牌搜索引擎索引工具了,我最早接触是10年夏天在淘宝实习的时候,作为一个小本科生,那是第一次接触到搜索引擎技术,当时的项目是基于Solr做的。再之后的工作虽然都是搜索相关的,但都是自己实现的索引系统。月初入职小麦公社,这边的搜索是基于Lucene做的,所以又开始用了一下Lucene。

一吐槽

话说是这样一个需求,增加一个生成的Field,不能分词,不用存储,需要设置特殊权重。看了一下Lucene的接口,TextField是分词的,而StringField是整体使用不分词的,因此自然选择了StringField。但是运行时问题就来了:

Exception in thread “main” java.lang.IllegalArgumentException: You cannot set an index-time boost on an unindexed field, or one that omits norms
仔细查了一下,虽然StringFieldTextField都是继承Field接口的,但实际上StringField属于”omits norms”,是不能调用setBoost(float b)接口的,而且坑爹的是那么多XxxField中只有TextField可以调用,简直无情啊。但还好Lucene还可以自己指定Field的分词、存储方法:

Field field = new Field(FIELD_NAME, "somestring", Field.Store.NO, Field.Index.NOT_ANALYZED);

这会运行时就妥儿妥儿的了,等等,为什么有删除线,这个构造函数竟然被标记成@Deprecated了?!强迫症不能忍啊,赶紧看看还有哪些没过时的接口。发现了这么一个org.apache.lucene.document.Field.Field(String name, String value, FieldType type),赶紧试试:

Field field = new Field(FIELD_NAME, "somestring", StringField.TYPE_NOT_STORED);

结果和直接使用StringField没啥区别,这应该就是另一种表达形式。

找了半天,还是没有找到不分词、不存储、可以设置boost且不deprecated的方法……这接口简直坑啊,毕竟搜索引擎中对字段的使用方式千变万化。而且StringField继承了接口,调用setBoost却抛出运行时异常到底是什么鬼?!真是不得不吐槽一下。没发现,实现这么简单的需求,总不能自定义一个Field吧,只能在方法钱加上@SuppressWarnings("deprecation")眼不见心不烦了……

二吐槽(2015.11.03更新)

话说今天做一个搜索需求,要求在某一分类下进行搜索,之前用的是Filter,但是IndexSeacher

void org.apache.lucene.search.IndexSearcher.search(Query query, Filter filter, Collector results) throws IOException

方法被标注为@Deprecated了,还是强迫症不能忍,看了一下这个方法的文档,是这么写的:

Deprecated. Use boolean queries with BooleanClause.Occur.FILTER clauses instead

嗯,说的挺明白的,说用就用呗,把Query Filter改成了一个BooleanClause.Occur.FILTERBooleanClause,其实挺简单的,但是评估结果时突然发现,只匹配到这个分句的结果全部被召回了,也就是说原先想表达的query是A AND B,从Filter改成BooleanClause之后,语义变成了A OR B,这完全不对啊。再仔细看BooleanClause.Occur.FILTER的文档:

Like MUST except that these clauses do not participate in scoring.

原来它只是一个不参与打分的MUST分句,这其实和Filter语义是不完全相同的,既然不能完全取代,怎么能直接给deprecated呢……这其实和一吐槽的问题差不多,就是新接口的语义是比旧接口小的。

google了一下,找到了当初加FILTER这个feature时的讨论

Now that we have weight-level control of whether scoring is needed or not, we could add a new clause type to BooleanQuery. It would behave like MUST exept that it would not participate in scoring.
Why do we need it given that we already have FilteredQuery? The idea is that by having a single query that performs conjunctions, we could potentially take better decisions. It’s not ready to replace FilteredQuery yet as FilteredQuery has handling of random-access filters that BooleanQuery doesn’t, but it’s a first step towards that direction and eventually FilteredQuery would just rewrite to a BooleanQuery.
I’ve been calling this new clause type FILTER so far, but feel free to propose a better name.

这个BooleanClause的目标其实就是必含这个clause并且不参与评分,和FilteredQuery不完全一样啊,简直莫名其妙,也许我们之前的用法不那么lucene吧……


文章作者: Odin
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Odin !
  目录