有的时候,我们一开始不可能准确地知道搜索的关键字在 Solr 中查询出的结果是什么,因此,Solr 还提供了几种类型的模糊查询。模糊匹配会在索引中对关键字进行非精确匹配。例如,有的人可能想要搜索某个前缀开始的单词(称为通配符查询),或者想要查询和关键字有一两个字母不相同的单词(称为模糊查询或编辑距离查询),或者你想要查询两个关键字,并且这两个关键字之间的距离不会大于某个最大值(称为临近查询)。总的说来,模糊匹配是查询中的一个强大的工具。
通配符查询
在 Solr 中最普遍使用的模糊查询就是使用通配符。假设你想要查询以 offic 开始的文档。下面列举出这个查询的几个版本:
-
查询语句: office OR officer OR official OR officiate OR … 这个列表中的单词是所有你以 offic 开头的单词。
因为你需要找到的所有匹配都在 Solr 索引中。因此,你可以使用星号(*)作为通配符来执行相同的功能:
-
查询语句: offi* 匹配 office, officer, official 等等。
除了放在关键字的最后,通配符也可以放到关键字中间,例如,如果你想要同时匹配 officer 和 offer:
-
查询语句: off*r 匹配 offer,officer,officiator 等。
星号通配符(*)表示匹配 0 个或多个字符。如果你只需要匹配一个字符,那么可以使用问号(?)通配符:
- 查询语句: off?r 匹配 offer 但是不匹配 officer。
以通配符为头进行查询
在 Solr 中使用通配符相当强大。但是,使用通配符进行查询也会带来很大的开销。一旦使用统配符的查询,那么在关键字中第一个通配符之前的部分需要在反向索引中全部查询出来。那后,每个查询出来的结果在逐一进行检查,看是否符合查询条件。正是因为这样,所以在统配符之前的字符越多,那么查询将会越快。例如,使用 engineer* 进行查询将不会带来很高的开销(因为这个查询在反向索引中不会找到太多的匹配),但是 e* 进行查询的开销就相当大,它将会匹配所有 e 开头的单词。
如果使用通配符开头的话,开销也会相当大。例如,你需要查询 ing 结尾的单词(像 caring,liking 和 smiling),那么将会带来严重的性能问题:
- 查询语句: *ing
如果你真的需要进行这样的查询,那么有一个现成的解决方案,这个解决方案就是添加 ReversedWildcardFilterFactory 到你的字段类型的分析链中(详细内容以后会讲到)。
ReversedWildcardFilterFactory 将会插入两条记录到 Solr 的索引中(一条是单词的正向文本内容,一条是单词的反向文本内容):
- 索引:caring/#gnirac liking/#gnikil smiling/#gnilims
当提交了 *ing 的查询之后,Solr 知道使用索引中的反向内容去查询,这样由通配符开头带来的性能问题就转换成了普通的通配符查询问题。
但是要注意,如果将这个特性打开的话,那么在 Solr 索引中的所有关键字都将会由两条索引记录,这无疑增加了索引的大小并且降低了整个查询的速度。因此,不建议打开这个功能,除非你的应用程序真的非常需要。
关于使用统配符查询的最后一点就是使用通配符只能对单独的关键字进行查询,而不能对短语进行查询,例如:
- 正常: softwar* eng?neering
- 不正常:”softwar* eng?neering”
如果你需要在短语中使用通配符,你将要把整个短语作为一个关键字存储到索引中,我们将会在以后讲解这个功能。
范围查询
Solr 也提供了在已知值之间的范围查询。当你需要查询某个范围之间的子集的时候,这个功能非常有用。例如,如果你只想查询 2012 年 2 月 2 日到 2012 年 8 月 2 日这六个月之间的文档,那么可以执行下面的查询:
- 查询语句:created:[2012-02-01T00:00.0Z TO 2012-08-02T00:00.0Z]
范围查询的语法结构也可以用在其它字段上:
- 查询语句: yearsOld:[18 TO 21] 匹配:18, 19, 20, 21
- 查询语句:title:[boat TO boulder] 匹配:boat, boil, book, boulder,等
- 查询语句:price:[12.99 TO 14.99] 匹配: 12.99, 13.000009, 14.99, 等
上面的范围查询都是放在一对方括号中,这成为被包含的范围查询语法(闭区间)。Solr 也支持不被包含的范围查询语法(开区间),这需要把查询范围放到大括号中:
- 查询语句:yearsOld:{18 TO 21} 匹配:19 和 20 但是不匹配 18 或 21
- 查询语句:yearsOld:{18 TO 21} Matches 19 and 20 but not 18 or 21
虽然看起来有点奇怪,但是 Solr 也提供了半包含的范围查询语法(半开区间):
- 查询语句: yearsOld:[18 TO 21} 匹配:18, 19, 20, 但是不匹配 21
范围查询的效率比查询单个关键字要低,但是为某个特定的范围进行检索提供了巨大的灵活性。需要注意的是,使用范围查询的返回结果是按照 Solr 索引进行排序的,也就是说是按照字典顺序。如果你创建了一个文本字段来存储数字,那么这些数字的返回顺序应该是这样:1, 11, 111, 12, 120, 13, 等。如果是数字类型的字段,那么将使用特殊的方式来进行索引这样可以弥补这个问题,但是要明白一点,在 Solr 索引中进行排序依赖于写入索引的时候,字段中的数据是如何被处理的。详细内容以后会说明。
模糊/编辑距离查询
对于很多搜索应用来说,很重要的功能是不仅仅需要精确匹配用户的文本内容。而且还允许一些灵活的变化,比如一些用户的拼写错误或相同单词的其它变体。Solr 通过基于 Damerau-Levenshtein 距离的编辑距离测量来支持这个功能,它将容忍 80% 以上的拼写错误。
Solr 提供的模糊编辑距离查询需要用到波浪符号(~):
- 查询语句: administrator~ 匹配: adminstrator, administrater, administratior,等
这个查询不仅匹配原始的关键字(administrator),还有其它与原始关键字有 2 个编辑距离的关键字。一个编辑距离表示增加,删除,取代或交换一个任意字符。关键字 adminstrator (在第六个字母出少了字符“i”)和原始关键字之间相差一个编辑距离,因为它删除了一个字符。同样 sadministrator 和原始关键字之间也是相差一个编辑距离,因为它在前面添加了一个字符。administratro 也与原始关键字有一个编辑距离,因为它将最后两个字符交换了顺序。
在编辑距离查询中也可以精确指定编辑距离:
- 查询语句:administrator~1 匹配一个编辑距离以内的内容。
- 查询语句:administrator~2 匹配两个编辑距离以内的内容(如果没有提供编辑距离的话,这个就是默认值)。
- 查询语句:administrator~N 匹配 N 个编辑距离以内的内容。
注意,任何编辑距离大于 2 的查询将会使查询速度变得很慢。如果编辑距离在 2 以内,那么将会使用很高效率的 Levenshtein 自动机(Levenshtein automaton),但是如果编辑距离大于 2,将会退回到更慢的编辑距离实现。
临近查询
在前面,我们看到了编辑距离查询是如何查找相似的关键字,而不是进行精确匹配。编辑距离的概念适用于关键字中字符的变换或短语中各个单词之间的变化。
如果你想要通过 Solr 的索引查询公司中所有员工的档案。一种方法是枚举出公司中所有可能的职位:
-
查询语句:”chief executive officer” OR “chief financial officer” OR “chief
marketing officer” OR “chief technology officer” OR …
当然,这种查询的前提是你需要知道公司中所有可能的职位,这当然不现实。另外的一种解决方案是单独搜索每个关键字:
- 查询语句: chief AND officer
这将会匹配所有可能的用例,但是同时也会匹配所有包含了这两个关键字的文档。例如:One chief concern arising from the incident was the safety of the police officer on duty。这个文档明显不符合我们的要求,但是如果使用上面的查询语句,那么将会返回这个文档。
Solr 提供了解决这种问题的方案:临近插叙。在上面的例子中,比较好的策略是请求 Solr 返回所有包含了关键字 chief 和关键字 officer 临近的文档。这可以通过下面的查询语句样例来实现:
- 查询语句: “chief officer”~1
解释:chief 和 officer 之间最多只能有一个距离
例子:”chief executive officer”, “chief financial officer” - 查询语句:”chief officer”~2
解释:chief 和 officer 之间最多只能有两个编辑距离
例子:”chief business development officer”, “officer chief” - 查询语句:”chief officer”~N
解释:查询 chief 和 officer 之间有 N 个编辑距离。
事实上,对短语进行精确匹配的查询语句 “chief development officer” 很容易改写成 “chief development officer”~0。这两个查询都返回相同的结果,因为在第二个查询语句中,编辑距离设置为 0,所以和精确查询得到的结果是相同的。这两种机制都需要使用到 Solr 中存储的关键字位置(前面的文章介绍过)来计算编辑距离。还有一点需要注意的是,临近查询并不是完全按照编辑距离的定义来进行查询,因为它的查询结果中,所有的关键字都必须存在。而编辑距离查询的定义中,可以对关键字进行删除和修改。
但是其它的编辑距离定义依旧保留,例如增加和换位。顺着这条线,你可能会注意到,你需要设置 2 进行临近查询的时候(”chief officer”~2)才能查询出文本 officer chief。这是因为第一次编辑将 chief 和 officer 修改成相同的位置;第二次编辑将 chief 才能将 chief 编辑到 officer 后面。这也再次说明了临近查询使用的并不是真正的编辑距离(在编辑距离中,位置互换的编辑距离只能算 1)。