Jak elegancko połączyć wyszukiwanie i sortowanie?

Cześć,

Powiedzmy, że mam model User.

[code=ruby]class User < ActiveRecord::Base
attr_accessible :last_visited_at, :friend_id, :blacklisted
has_many :friends
end

class Searcher

end[/code]
Chciałbym wyszukiwać użytkowników i zwracać posortowaną listę wg kryteriów(z największą ilością punktów wyświetl u góry):

  1. Jeżeli dana fraza występuje w opisie dodaj 5 punktów.
  2. jeżeli widziany w ciągu ostatnich 3 dni dodaj 2 punkty
  3. jeżeli ma jakichkolwiek znajomych dodać 3 punkty
  4. jeżeli zaznaczony blacklisted przesuń na koniec kolejki

Zastanawiam się jak do tego powinienem podejść, stworzyć services/searcher.rb ?

Najpierw wyszukać po frazie wszystkich. Ale jak i w którym momencie najlepiej przeprowadzić sortowanie?

Mogę się mylić (bo nie wdrażałem takich rzeczy na poważnie), ale Sphinx czy ElasticSearch pozwolą Tobie stworzyć odpowiednie indeksy i sposoby na odpytywanie.

http://railscasts.com/episodes/306-elasticsearch-part-1, jest jeszcze część druga. Elastic search jest obecnie najmodniejszym rozwiązaniem.

[code=ruby] Tire.search(‘users’, load: true) do
query do
custom_filters_score do
query { all }

      filter do
        filter :range, last_contact_at: { gte: 3.days.ago }
        boost 2
      end

      score_mode :total
    end
  end
end.results[/code]

Udało mi się znaleźć http://www.elasticsearch.org/guide/reference/query-dsl/custom-filters-score-query/ odpowiedni filtr w ElasticSearch.

Mam teraz problem z zaimplementowaniem punktu 1 i 3 i 4 czyli sprawdzenie ilu znajomych ma dany rekrod. Macie jakieś pomysły jak się do tego zabrać?

Do 3 pkt najlepiej zrób sobie counter cache na relacji znajomych i dodaj go do indeksu, wtedy możesz już analogicznie jak wyżej łatwo to filtrować zakresem.

Albo opcja druga, do indeksu dorzuć też id znajomych

indexes :friends, type: 'object' do indexes :id, type: 'string', index: 'not_analyzed' end
A potem dodajesz filtr

 filter :exists, field: "friends.id"

Hej, został mi tylko jeden punkt do zrobienia.

Modele

[code=ruby]class ProfileTag < ActiveRecord::Base
attr_accessible :tag_id, :profile_id
belongs_to :tag
belongs_to :profile
end

class Tag < ActiveRecord::Base
attr_accessible :name , :focus(boolean)
has_many :profile_tags
has_many :profiles
end

class Profile < ActiveRecord::Base
has_many :tags, through: :profile_tags
has_many :profile_tags

def to_indexed_json
{ name: self.name,
company_type: self.company.company_type,
}.to_json
end
end[/code]

[code=ruby] Tire.search ‘profiles’ do
query do
custom_filters_score do
query { all }

      # If currently available, give a score of 2
      filter do
        filter :range, available_at: { gte: Date.today }
        boost 2
      end

      # If Profile is a Profile of a Company Member, give this profile a score of 10.
      filter do
        filter :term, { company_type: 'intern' }
        boost 10
      end

      # If at least personally known by one person, give a score of 3, not more
      filter do
        filter :range, friends_count: { from: 1 }
        boost 3
      end

      # If blacklisted, show this profile on the end of the result list, not depending on the score.
      filter do
        filter :term, { blacklisted: true }
        boost -100
      end

      # Foreach Matching "Tag", give a score of 4 if tag has flag focus give a score of 5.

      score_mode :total
    end
  end[/code]

Zastanawiam się jak w tym przypadku wyszukiwać tagi, czy przekać je w indexie ‘profiles’ czy zrobić nowy index ‘tags’ z atrybutem ‘profile_id’(tylko jak potem je połączyć?

Możesz zrobić coś takiego:

def to_indexed_json { name: self.name, company_type: self.company.company_type, }.to_json(:include=>{:tags=>{:only=>[:id, :name}} ) end
A potem powinno się dać szukać poprzez:

 filter :terms, "tags.name" =>  ["tag1", "tag2"]