我们现在能够任意搜索索引文档中的单词。但是,搜索只是检索我们正在寻找的信息的第一步。在我们获得文档列表之后,我们需要确定每个文档中足够重要的内容,以确定其相对于其他匹配文档的位置。这个问题通常在搜索领域被称为相关性,而确定一篇文章是否比另一篇文章更相关的一种方法是哪篇文章最近更新过。让我们看看如何将其作为搜索结果的一部分。
如果您还记得第 3 章,Redis SORT 调用允许我们对 LIST 或 SET 的内容进行排序,可能会引用外部数据。对于 Fake Garage Startup 知识库中的每篇文章,我们还将包含一个 HASH,其中存储有关该文章的信息。我们将存储的有关文章的信息包括标题、创建时间戳、文章上次更新的时间戳以及文档的 ID。图 7.4 中显示了一个示例文档。
使用这种格式存储的文档,我们可以使用 SORT 命令按几个不同的属性进行排序。我们一直在为我们的结果 SET 设置过期时间,以便在使用完毕后不久将其清除。但是,对于我们最终的 SORT 后的结果,我们可以将该结果保留更长时间,同时允许重新排序,甚至对结果进行分页,而无需再次执行搜索。用于集成此类缓存和重新排序的函数可以在以下列表中看到。
def search_and_sort(conn, query, id=None, ttl=300, sort="-updated", start=0, num=20):
我们可以选择接受以前的结果 ID、一种对结果进行排序的方式以及对结果进行分页的选项。
desc = sort.startswith('-') sort = sort.lstrip('-') by = "kb:doc:*->" + sort
确定要按哪个属性排序,以及是升序还是降序排序。
alpha = sort not in ('updated', 'id', 'created')
我们需要告诉 Redis 我们是按数字还是按字母顺序排序。
if id and not conn.expire(id, ttl): id = None
如果存在先前的结果,请尝试更新其过期时间(如果它仍然存在)。
if not id: id = parse_and_search(conn, query, ttl=ttl)
如果我们没有过去的搜索 ID,或者我们的结果已过期,则执行搜索。
pipeline = conn.pipeline(True)
pipeline.scard('idx:' + id)
获取结果总数。
pipeline.sort('idx:' + id, by=by, alpha=alpha, desc=desc, start=start, num=num)
按正确的列对结果列表进行排序,并仅获取我们想要的结果。
results = pipeline.execute()
return results[0], results[1], id
返回结果中的项目数、我们想要的结果以及结果的 ID,以便我们稍后再次获取它们。
在搜索和排序时,我们可以通过更新 start 和 num 参数来对结果进行分页;使用 sort 参数更改排序属性(和顺序);使用 ttl 参数缓存结果更长时间或更短时间;并使用 id 参数引用以前的搜索结果(以节省时间)。
虽然这些函数无法让我们创建一个与 Google 竞争的搜索引擎,但这个问题和解决方案是我首先使用 Redis 的原因。对 SORT 的限制导致使用 ZSETs 来支持更复杂的文档排序形式,包括组合分数以进行复合排序。