【问题标题】:Leaderboard ranking via elasticsearch (with tire)通过 elasticsearch 进行排行榜排名(带轮胎)
【发布时间】:2013-08-28 19:29:57
【问题描述】:

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

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 |

是否有可能通过 elasticsearch 以某种方式计算此排名?

【问题讨论】:

  • 你有没有想过使用redis?它非常适合这样的事情。对于 elasticsearch,我会阅读他们的 sort 文档:elasticsearch.org/guide/reference/api/search/sort
  • 排名是相对于当前结果计算的,还是整个数据集的总和?例如,在您的数据集中还有 10 票的“莎拉”,因此谁会使用不同的查询/过滤器排名 1?
  • 正如@DamienRoche 所说,Redis 是处理这类事情的完美工具(假设票数和排名经常变化)。我不会为此想到 ElasticSearch。

标签: ruby-on-rails elasticsearch tire


【解决方案1】:

我不认为 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

【讨论】:

  • 如果 OP 也在寻找分页和排名,那么在 ruby​​ 中排序可能不是一个选项。
【解决方案2】:

Redis 确实是排行榜的理想解决方案。虽然它引入了另一种技术,但如果您使用的是 AWS,请注意 ElastiCache 托管的 Redis 是 just launched this week

通用 Redis 命令是:

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

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

zrevrange votes 0 -1

有关详细信息,请参阅Redis docs for ZREVRANGE

对于 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)

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

【讨论】:

    猜你喜欢
    • 2020-06-15
    • 1970-01-01
    • 2015-02-26
    • 2018-01-31
    • 1970-01-01
    • 2021-09-21
    • 2021-10-07
    • 1970-01-01
    • 2019-03-22
    相关资源
    最近更新 更多