4

我有一个映射可以归结为以下内容(删除了不相关的字段):

mapping 
  indexes :id, type: 'integer', index: :not_analyze        
  indexes :first_name, boost: 5, type: 'string', analyzer: 'snowball'
  indexes :votes, type: 'integer', index: :not_analyzed
end

目前我正在通过 postgres 计算排名,因此给出以下条目:

| first_name | votes |
----------------------
| Andy       |     5 |
| Barry      |     8 |
| Carl       |     5 |
| Derek      |     1 |

使用 postgres,我可以获得以下信息:

| first_name | votes | rank |
-----------------------------
| Barry      |     8 |    1 |
| Andy       |     5 |    2 |
| Carl       |     5 |    2 |
| Derek      |     1 |    4 |

是否有可能通过弹性搜索以某种方式计算此排名?

4

2 回答 2

3

Redis 确实是排行榜的理想解决方案。虽然它正在引入另一种技术,但如果您使用的是 AWS,请注意 ElastiCache 托管 Redis 是本周刚刚推出的。

通用 Redis 命令将是:

zadd votes 5 "Andy"
zadd votes 8 "Barry"
zadd votes 5 "Carl"
zadd votes 1 "Derek"

然后获得票数最高的排行榜:

zrevrange votes 0 -1

有关更多详细信息,请参阅ZREVRANGE 的 Redis 文档。

对于 Ruby on Rails,我鼓励您查看我的redis-objects gem,它很受欢迎,因为它很容易与 ActiveRecord 集成。假设您有一个包含votes如图所示列的表,您可以在保存时更新排名:

class User < ActiveRecord::Base
  include Redis::Objects
  sorted_set :rank, global: true

  after_save :update_rank
  def update_rank
    self.class.rank[id] = votes
  end
end

然后检索排行榜:

User.rank.revrange(0, -1)

在此示例中,这将返回id值,然后您可以使用这些值来检索记录,如下所示。(您也可以存储first_name或其他一些独特的价值。)

ids = User.rank.revrange(0, -1)
users = User.where(id: ids).all

revrange您可以通过传递不同的开始/结束值来对结果进行分页:

User.rank.revrange(0, 9)
User.rank.revrange(10, 19)

您可以轻松地将其包装self.在 User 中的一个方法中,该方法从 Redis 检索排名页面并相应地返回数据库记录。

于 2013-09-06T17:20:19.217 回答
3

我不相信 ElasticSearch 是这样做的地方,因为更新单个文档需要重新计算所有排名值。不可能,据我所知。

相反,一旦你得到结果,你就可以使用 Ruby 来计算排名,如下所示:

scores = {:a=>5, :b=>8, :c=>5, :d=>1}
scores.values.sort{|a,b| a <=> b}.tap do |sorted_scores|
  sorted_scores.each{|vote| puts sorted_scores.index(vote)+1 }
end
于 2013-09-05T17:58:18.600 回答