Рейтинг лидеров через elasticsearch (с шиной)

У меня есть сопоставление, которое сводится к следующему (удалены несвязанные поля):

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?


person Jim Neath    schedule 28.08.2013    source источник
comment
Вы думали об использовании Redis? Это идеально подходит для чего-то вроде этого. Для elasticsearch я бы прочитал их sort документацию: elasticsearch.org /guide/reference/api/search/sort   -  person Damien Roche    schedule 29.08.2013
comment
Вычисляется ли ранг относительно текущих результатов или в целом по всему набору данных? Например, есть ли в вашем наборе данных также «Сара» с 10 голосами, которая, следовательно, заняла бы 1 место с другим запросом/фильтром?   -  person Phil    schedule 05.09.2013
comment
Как сказал @DamienRoche, Redis — идеальный инструмент для такого рода вещей (при условии, что количество голосов и, следовательно, рейтинг часто меняются). Я бы не подумал об ElasticSearch для этого.   -  person Damien    schedule 06.09.2013


Ответы (2)


Я не верю, что 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
person emptyflask    schedule 05.09.2013
comment
Сортировка в ruby ​​может быть неприемлемой, если OP также ищет разбиение на страницы вместе с ранжированием. - person rubish; 05.09.2013

Redis — действительно идеальное решение для списков лидеров. Хотя это представляет другую технологию, если вы используете AWS, обратите внимание, что Redis, управляемый ElastiCache, был только что запущен на этой неделе.

Общие команды Redis:

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

Затем, чтобы получить таблицу лидеров с наибольшим количеством голосов как с самым высоким рейтингом:

zrevrange votes 0 -1

Дополнительные сведения см. в документах Redis для ZREVRANGE.

Что касается Ruby on Rails, я бы посоветовал вам взглянуть на мой популярный гем redis-objects. так как он легко интегрируется с 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 и возвращает соответствующие записи БД.

person nateware    schedule 06.09.2013