Solr 的核心就是搜索
编者注:本文为历史博文归档;涉及 JDK、框架与工具链版本请以当前官方文档为准。引用外链图片可能失效,阅读时请注意时效性。
原文链接:http://www.aptusource.org/2014/06/searching-is-what-its-all-about/
Solr 的核心功能在于其强大的查询处理能力。在本文中,你将深入了解 Solr 的查询处理机制,这有助于理解为何 Solr 被视为强大的搜索技术。
阅读时,请重点关注每个查询与 Solr 返回文档之间的关联,以及结果文档的排序方式。这将帮助你建立搜索引擎式的思维模式,后续我们会逐渐覆盖核心的搜索概念。
探索 Solr 的查询表单
你可能已经使用过 Solr 的查询表单来执行“查找所有文档”的查询。现在,我们来快速浏览该表单的其他特性,以便对 Solr 支持的查询类型有个直观的了解。下图对表单的重点部分进行了注释,请花时间逐个了解:

在上图中,我们编写了一个查询。如果使用《[Solr] 快速入门》中添加的文档,结果中将返回两个示例文档。建议你参照上图填写表单,并在自己的环境中执行一次,验证是否真的返回了两个示例文档。
所有与 Solr 核心服务的交互均通过 HTTP 请求完成。当你填写查询表单并点击查询后,会向 Solr 发起一个 HTTP GET 请求。下图展示了点击查询后发起的 HTTP GET 请求细节。注意,真正的 HTTP GET 请求是没有换行符的,此处使用换行符是为了更清晰地展示每个参数:

更多查询示例?
后续我们会更深入地理解查询。如果你现在就想查看更多的查询示例,建议使用浏览器打开 $SOLR_INSTALL/docs/tutorial.html 这个 HTML 文档,那里提供了使用示例文档进行查询的更多案例。
需要注意的是,Solr 的这个查询表单并非为最终用户设计。它是为开发人员和管理员提供的一种发起 HTTP 查询的简便方式,无需手动编写 HTTP 请求或使用客户端应用程序。但我们必须清楚:在编写基于 Solr 的应用程序时,你有责任开发用户界面(UI)。本文后面将会提到,Solr 提供了可定制的查询 UI 组件,可以帮助你构建所需的查询界面。
查询返回结果
前面我们了解了发送到 Solr 的数据,接下来看看 Solr 返回的结果。Solr 返回的是匹配查询条件的文档,以及能让 Solr 客户端提供高质量搜索体验的附加信息。数据处理是通过 Solr 客户端完成的,Solr 本身只返回原始数据和功能特性(features),你需要自行为用户创建高质量的搜索体验。
下图展示了上一节例子返回的结果。可以看到,返回的是 XML 格式,并且按价格从低到高排序。每个文档都包含了 iPod 关键字。结果中没有分页,因为一共只有两条数据。

目前为止,我们只看过 XML 格式的返回值,但 Solr 还支持其他格式,例如:
- CSV(逗号分隔值)
- JSON(JavaScript Object Notation)
- 满足流行语言规范的格式
例如,Solr 可以返回 Python 对象格式,可通过 eval 方法将响应安全地转换为 Python 对象。
检索排名机制
Solr 的检索过程与数据库检索及其他 NoSQL 数据库检索的主要不同之处在于检索排名。Solr 通过检索的相关度(Relevance)对文档排序,相关度最高的文档最先列出。
我们使用前面已添加索引的例子来看看检索排名是如何工作的:
- 最开始,在
q文本框输入iPod,在fl文本域输入name,features,score,然后点击执行。 - 系统将返回三个文档,并且按照评分(score)降序排列。
- 检查查询结果,看看是否认同这个简单查询的排序逻辑。
直观看来,这样的排序合乎情理:关键字 iPod 在第一个文档中一共出现了三次(两次在 name 中,一次在 features 中);而在另外两个文档中都只出现了一次。score 字段的数字用于 Lucene 内部进行相关度排序,这个值在不同的查询之间没有可比性。每个匹配查询的文档都会分配一个针对该查询的相关性得分,结果按照得分降序排列。
接下来,将查询关键字改为 iPod power,你将会看到返回相同的三个文档,并且顺序也和之前一样。这是因为所有三个文档的 name 或 features 字段中都包含了查询关键字。但是我们可以看到前两个文档的得分非常接近:第二个查询的得分是 1.521 和 1.398,第一个查询的得分是 1.333 和 0.770。这也是合理的,因为 power 在第二个文档中出现了两次,所以用 iPod power 进行查询的得分应该比通过 iPod 进行查询的得分要高。
现在,将查询关键字改为 iPod power^2,这将会把 power 关键字的权重提升为 2。简单来说,这意味着在进行查询时,power 关键字比 iPod 关键字重要两倍(默认权重是 1)。执行查询后,返回了相同的三个文档,但是它们的顺序有所不同。现在,结果中最顶部的文档是 "Belkin Mobile Power Cord for iPod w/ Dock",因为在它的 name 和 features 字段中包含了 power 关键字。我们之前说过,在这次查询中 Solr 会认为 power 关键字比 iPod 重要两倍。
现在,你已经感受过了什么是搜索排名。接下来我们看看搜索处理的其他特性,先来看看如何使用分页和排序。
分页与排序
我们的样例中,Solr 只索引了 32 个文档,但真实的 Solr 项目一般会包含数百万的文档。想象一下将 Solr 应用到一个电子产品的网上商店,如果使用 iPod 关键字进行查询,可能会匹配到几千个产品和周边配件。要确保快速返回结果,特别是在带宽有限的手持设备上面,你不可能一次性返回几千条数据,即便将最相关的数据一次性返回也是不行的。
分页
解决方案是通过分页来返回数据,并通过导航来请求更多页的数据。在 Solr 查询处理中,分页是核心概念,在每个查询中都需要控制每页的记录数(rows)和开始位置(start)。
- 如果在请求中没有设置,Solr 使用的默认每页记录数是 10。
- 你可以在发起查询请求的时候通过
rows参数进行控制。 - 要请求下一页的结果,可以通过每页记录数来增加
start参数的值。例如,如果你现在返回了第一页的结果(start=0),现在你要查询下一页,可以通过每页记录数来增加start参数的值,例如start=10。
要记住很重要的一点:让每页记录数在满足需求的情况下尽可能小。因为底层的 Lucene 并未针对一次性返回大量文档进行优化。相反,底层的 Lucene 是对查询过程进行了优化,因此底层的数据结构是设计用于返回最佳匹配和最高评分的文档。
一旦搜索结果确定下来,Solr 需要重新构建每个文档结构,大多数情况下是从磁盘读取数据。为了效率,它使用了智能缓存,但是相比查询过程来说,构建查询结果是一个比较慢的过程,尤其是当每页记录数大的时候。因此,为了更高的效率,在 Solr 中要让每页记录数尽可能的小。
排序
就像前文所述,返回结果是按评分来进行降序排列(从高到低)。但是,你也可以请求 Solr 的返回结果按照某个字段来进行排序。前面已经看过了结果按照价格来升序排列,产品价格最低的排在最前面。
排序和分页具有很强的相关性,因为排序规则决定了页面的位置。为了帮助你更好地了解排序和分页,我们考虑一下这个问题:如果只设置了分页规则而没有设置排序规则,Solr 是否每次都会返回固定的结果?
表面上看来,这个问题的结果很明显,因为如果没有设置排序规则,那么结果就会按照评分来进行降序排列。但是,如果查询的结果中有相同的评分呢?例如,如果你查询 inStock:true,那么所有匹配的文档都有相同的评分;你可以通过查询表单来验证这个结果。
事实证明,尽管评分一致,Solr 也能保持每次返回值的排序是固定的。这是因为 Solr 找到所有匹配的文档后再应用排序和分页。Solr 将会保持跟踪整个文档集的排序和分页。另外,顺便说一下,如果所有文档的评分都一样,那么将会返回索引顺序,这是基于内部由 Lucene 管理的文档 ID。这个内部的文档 ID 大致等同于文档被索引的顺序,但是你不需要依赖这个值来进行排序,因为当你的索引发生变更时这个值有可能改变。
说明:本文内容基于较早期版本的 Solr(参考原文发布时间为 2014 年)。现代版本的 Solr 在默认返回格式(如 JSON)、管理界面 UI 及部分 API 行为上可能有所变化,具体请以当前官方文档为准。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/solr-de-he-xin-jiu-shi-sou-suo.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。